//-----------------------------------------------------------------------
// <copyright file="GetVSSFiles.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>Andy Reeves</author>
// <email>andyr</email>
// <date>2004-03-23</date>
// <summary>Performs a Get from the Visual SourceSafe specified.</summary>
//-----------------------------------------------------------------------
namespace Microsoft.Sdc.Tasks.SourceSafe
{
    using System;
    using System.Globalization;
    using System.IO;
    using Microsoft.Win32;
    using Microsoft.Sdc.Tasks.Configuration;
    using Microsoft.Build.Framework;

    /// <summary>
    /// Performs a Get from the Visual SourceSafe specified.
    /// </summary>
    /// <remarks>
    /// <code><![CDATA[
    /// <SourceSafe.GetVSSFiles
    ///             Database="database"
    ///             Username="username" 
    ///             Password="password" 
    ///             GetParameter="getParameter" 
    ///             Label="label" 
    ///             OnlyIncremementRevision="onlyIncrementRevision" 
    ///             Project="project"
    ///             VersionNumber="versionNumber"
    ///             VersionNumberConfigFileLocation="versionNumberConfigFileLocation"
    ///             VersionNumberConfigVSSLocation="versionNumberConfigVSSLocation"
    ///             WorkingDirectory="workingDirectory" >
    ///             SSVersion="ssversion">
    /// </SourceSafe.GetVSSFiles>
    /// ]]></code>
    /// <para>where:</para>
    /// <para><i>database</i></para>
    /// <para>The VSS database location. This database must exist and must be a valid VSS database.</para>
    /// <para><i>username</i></para>
    /// <para>The username that will be used when labelling VSS</para>
    /// <para><i>password</i></para>
    /// <para>The password for the username specified in <i>username</i></para>
    /// <para><i>getParameter</i></para>
    /// <para>Allows a specific version of files/projects to be supplied</para>
    /// <para><i>label</i></para>
    /// <para>Label to use when getting a particular version.</para>
    /// <para><i>onlyIncremementRevision</i></para>
    /// <para>Force Revision only change. So 2.0.1188.14 would be 2.0.1188.15 on the next automatic label from the source control system.</para>
    /// <para><i>project</i></para>
    /// <para>The VSS style syntax for project location.</para>
    /// <para><i>versionNumber</i></para>
    /// <para>If specified then $(VersionNumber) is set to this value and replaced as appropriate in the xml config file</para>
    /// <para><i>versionNumberConfigFileLocation</i></para>
    /// <para>Fully Qualified file name to the xml config file. The file contains, in Xml format, the major, minor, build and revision number.</para>
    /// <para><i>ssversion</i></para>
    /// <para>The SourceSafe version to use. Default is 6d. Supports 6d and 2005</para>
    /// 
    /// For example:
    /// <example>
    /// <code><![CDATA[
    /// <?xml version="1.0" encoding="utf-8"?>
    ///     <configuration>
    ///         <appSettings>
    ///             <add key="major" value="1" />
    ///             <add key="minor" value="2" />
    ///             <add key="build" value="1354" />
    ///             <add key="revision" value="155" />
    ///         </appSettings>
    ///     </configuration>
    /// ]]></code>    
    /// </example>
    ///
    /// <para><i>versionNumberConfigVSSLocation</i></para>
    /// <para>The path to the config file specified in <i>versionNumberConfigFileLocation</i> inside VSS, using VSS-style notation.</para>
    /// <para><i>workingDirectory</i></para>
    /// <para>The full path to the working directory on the local machine for the cod</para>
    /// </remarks>
    /// <example>
    /// <code><![CDATA[
    /// <Project>
    ///     <Target Name="Test" >
    ///         <SourceSafe.GetVSSFiles
    ///             Database="database"
    ///             GetParameter="getParameter" 
    ///             Label="label" 
    ///             OnlyIncremementRevision="onlyIncrementRevision" 
    ///             Project="project"
    ///             VersionNumber="versionNumber"
    ///             VersionNumberConfigFileLocation="versionNumberConfigFileLocation"
    ///             VersionNumberConfigVSSLocation="versionNumberConfigVSSLocation"
    ///             WorkingDirectory="workingDirectory" >
    ///         </SourceSafe.GetVSSFiles> 
    ///     </Target>
    /// </Project>
    /// ]]></code>    
    /// </example>
    public class GetVSSFiles : TaskBase
    {
        private string project;
        private string database;
        private string username;
        private string password;
        private string label;
        private string getParameter;
        private string workingDirectory;
        private string buildNumberFull;
        private string versionNumberConfigFileLocation;
        private string versionNumberConfigVSSLocation;
        private bool onlyIncrementRevision;
        private string ssafeVersion = "6d";
        private ShellExecute shellExecute;

        /// <summary>
        /// Gets or sets the SourceSafe version.
        /// </summary>
        /// <value>The SS version.</value>
        public string SSVersion
        {
            get { return this.ssafeVersion; }
            set { this.ssafeVersion = value; }
        }

        /// <summary>
        /// Gets or sets the project.
        /// </summary>
        /// <value>The project.</value>
        public string Project
        {
            get { return (this.project ?? String.Empty); }

            set { this.project = value; }
        }

        /// <summary>
        /// Gets or sets the database.
        /// </summary>
        /// <value>The database.</value>
        public string Database
        {
            get { return (this.database ?? String.Empty); }

            set { this.database = value; }
        }

        /// <summary>
        /// Gets or sets the username.
        /// </summary>
        /// <value>The username.</value>
        public string Username
        {
            get { return (this.username ?? String.Empty); }

            set { this.username = value; }
        }

        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        /// <value>The password.</value>
        public string Password
        {
            get { return (this.password ?? String.Empty); }

            set { this.password = value; }
        }

        /// <summary>
        /// Gets or sets the label.
        /// </summary>
        /// <value>The label.</value>
        public string Label
        {
            get { return (this.label ?? String.Empty); }

            set { this.label = value; }
        }

        /// <summary>
        /// Gets or sets the get parameter.
        /// </summary>
        /// <value>The get parameter.</value>
        public string GetParameter
        {
            get { return (this.getParameter ?? String.Empty); }

            set { this.getParameter = value; }
        }

        /// <summary>
        /// Gets or sets the working directory.
        /// </summary>
        /// <value>The working directory.</value>
        public string WorkingDirectory
        {
            get { return (this.workingDirectory ?? String.Empty); }

            set { this.workingDirectory = value; }
        }

        /// <summary>
        /// Gets or sets the version number.
        /// </summary>
        /// <value>The version number.</value>
        public string VersionNumber
        {
            get { return (this.buildNumberFull ?? String.Empty); }

            set { this.buildNumberFull = value; }
        }

        /// <summary>
        /// Gets or sets the version number config file location.
        /// </summary>
        /// <value>The version number config file location.</value>
        public string VersionNumberConfigFileLocation
        {
            get { return (this.versionNumberConfigFileLocation ?? String.Empty); }

            set { this.versionNumberConfigFileLocation = value; }
        }

        /// <summary>
        /// Gets or sets the version number config VSS location.
        /// </summary>
        /// <value>The version number config VSS location.</value>
        public string VersionNumberConfigVSSLocation
        {
            get { return (this.versionNumberConfigVSSLocation ?? String.Empty); }

            set { this.versionNumberConfigVSSLocation = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [only increment revision].
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [only increment revision]; otherwise, <c>false</c>.
        /// </value>
        public bool OnlyIncrementRevision
        {
            get { return this.onlyIncrementRevision; }

            set { this.onlyIncrementRevision = value; }
        }

        /// <summary>
        /// Performs the action of this task.
        /// </summary>
        protected override void InternalExecute()
        {
            if (this.GetVersionInformation() == false)
            {
                return;
            }

            this.VSSGet();
        }

        private bool GetVersionInformation()
        {
            RegistryKey vssKey;
            this.shellExecute = new ShellExecute();
            switch (this.SSVersion)
            {
                case "6d":
                    vssKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual SourceSafe Server");
                    if (vssKey == null)
                    {
                        // couldn't find the Server key so try the other one
                        vssKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual SourceSafe");
                    }

                    if (vssKey != null)
                    {
                        string pathToSourceSafe = Convert.ToString(vssKey.GetValue("ProductDir"), CultureInfo.InvariantCulture);
                        this.shellExecute.Filename = Path.Combine(pathToSourceSafe, @"win32\ss.exe");
                        vssKey.Close();
                    }
                    else
                    {
                        Log.LogMessage(MessageImportance.Low, string.Format("Version is 6d but registry keys not found at: {0} or {1}. Assuming ss.exe is in the Path", @"SOFTWARE\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual SourceSafe Server", @"SOFTWARE\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual SourceSafe"));
                        this.shellExecute.Filename = "ss.exe";
                    }

                    break;
                case "2005":
                    vssKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\VisualStudio\8.0\Setup\VS\VSS");

                    if (vssKey != null)
                    {
                        string pathToSourceSafe = Convert.ToString(vssKey.GetValue("ProductDir"), CultureInfo.InvariantCulture);
                        this.shellExecute.Filename = Path.Combine(pathToSourceSafe, "ss.exe");
                        vssKey.Close();
                    }
                    else
                    {
                        Log.LogMessage(MessageImportance.Low, string.Format("Version is 2005 but registry key not found at: {0}. Assuming ss.exe is in the Path", @"SOFTWARE\Microsoft\VisualStudio\8.0\Setup\VS\VSS"));
                        this.shellExecute.Filename = "ss.exe";
                    }

                    break;
                default:
                    Log.LogError("Invalid SSVersion. Valid options are 6d or 2005");
                    return false;
            }

            this.shellExecute.EnvironmentVariables["SSDIR"] = this.Database;
            if (string.IsNullOrEmpty(this.Username))
            {
                this.Username = Environment.UserName;
            }

            this.shellExecute.EnvironmentVariables["SSUSER"] = this.Username;
            if (string.IsNullOrEmpty(this.Password) == false)
            {
                this.shellExecute.EnvironmentVariables["SSPWD"] = this.Password;
            }

            Log.LogMessage(MessageImportance.Low, string.Format("Using UserName: {0} and Password: {1}", this.username, this.Password));
            return true;
        }

        /// <summary>
        /// VSSs the get.
        /// </summary>
        private void VSSGet()
        {
            if (string.IsNullOrEmpty(this.Label))
            {
                if (string.IsNullOrEmpty(this.getParameter) == false)
                {
                    this.label = "-VL" + this.getParameter;
                }
            }
            else
            {
                this.Label = "-VL\"" + this.Label + "\"";
            }

            Directory.CreateDirectory(Path.Combine(Environment.CurrentDirectory, "Output"));
            string getLocation;
            if (string.IsNullOrEmpty(this.WorkingDirectory) == false)
            {
                getLocation = this.workingDirectory;
            }
            else
            {
                getLocation = Path.Combine(Environment.CurrentDirectory, "..");
            }

            string args = String.Format(CultureInfo.InvariantCulture, " Get -GL{0} -I- -R -O@\"{1}\"\\VSSGet.txt {2} \"{3}\"", getLocation, Path.Combine(Environment.CurrentDirectory, ".\\Output"), this.Label, this.Project);

            Log.LogMessageFromResources("Vss.Connect");
            if (string.IsNullOrEmpty(this.Label))
            {
                Log.LogMessageFromResources("Vss.GetLatest");
            }
            else
            {
                Log.LogMessageFromResources("Vss.Get", this.label.Substring(3));
            }

            this.shellExecute.Arguments = args;

            int returncode = this.shellExecute.Execute();

            // We need to keep old behaviour of throwing a Vss.RegistryKeyNotFound exception when
            // the exe can't be found for backward compatability
            if (returncode != 0)
            {
                Log.LogMessage(MessageImportance.High, this.shellExecute.StandardError);
                if (returncode == 1)
                {
                    throw new TaskException("Vss.RegistryKeyNotFound");
                }
                else
                {
                    throw new TaskException("Vss.Error");
                }
            }
        }
    }
}